/*
 * Copyright (C) Jenly, CameraScan Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ashlikun.camera.scan;

import android.Manifest;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.view.PreviewView;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;

import com.ashlikun.camera.scan.analyze.Analyzer;
import com.ashlikun.camera.scan.util.CameraXPermissionUtils;

/**
 * 相机扫描基类；{@link BaseCameraScanView} 内部持有{@link CameraScan}，便于快速实现扫描识别。
 * <p>
 * 快速实现扫描识别主要有以下几种方式：
 * <p>
 * 3、继承{@link CameraScan}自己实现一个，可参照默认实现类{@link BaseCameraScan}，其他步骤同方式2。（高级用法，谨慎使用）
 *
 */
public abstract class BaseCameraScanView<T> extends FrameLayout implements ScanCallback.OnScanResultCallback<T>, LifecycleEventObserver {
    /**
     * 相机权限请求代码
     */
    private static final int CAMERA_PERMISSION_REQUEST_CODE = 0x86;
    /**
     * 预览视图
     */
    protected PreviewView previewView;
    /**
     * CameraScan
     */
    private CameraScan<T> mCameraScan;
    /**
     * 保证避免多次调用start()
     */
    private boolean isGoRequestPermission = false;

    private InitCameraScan<T> initCameraScanCall;
    private ScanResultCallback<T> scanResultCallback;
    /**
     * 相机打开回调
     */
    private ScanCallback.OnCameraOpenCallback mOnCameraOpenCallback;

    public BaseCameraScanView(@NonNull Context context) {
        this(context, null);
    }

    public BaseCameraScanView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BaseCameraScanView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public BaseCameraScanView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        inflateView(context, attrs, defStyleAttr, defStyleRes);
    }

    public interface InitCameraScan<T> {
        void call(CameraScan<T> cameraScan);
    }

    public interface ScanResultCallback<T> {
        void call(@NonNull AnalyzeResult<T> result);
    }

    public void setInitCameraScanCall(InitCameraScan<T> initCameraScanCall) {
        this.initCameraScanCall = initCameraScanCall;
    }

    public void setScanResultCallback(ScanResultCallback<T> scanResultCallback) {
        this.scanResultCallback = scanResultCallback;
    }

    protected void inflateView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BaseCameraScanView);
        boolean camera_no_layout = typedArray.getBoolean(R.styleable.BaseCameraScanView_camera_no_layout, false);
        typedArray.recycle();
        if (!camera_no_layout) {
            int layoutId = getLayoutId();
            if (layoutId != NO_ID) {
                LayoutInflater.from(getContext()).inflate(getLayoutId(), this);
            }
        }
    }

    /***
     * 绑定生命周期并启动相机
     */
    public void synchLifeStart(Lifecycle lifecycle) {
        lifecycle.addObserver(this);

    }

    /**
     * 初始化
     */
    public void initUI(LifecycleOwner owner) {
        previewView = findViewById(getPreviewViewId());
        mCameraScan = createCameraScan(owner, previewView);
        initCameraScan(mCameraScan);
        if (!isGoRequestPermission) {
            startCamera();
        }
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
        switch (event) {
            case ON_CREATE:
                onCreate(source);
                break;
            case ON_START:
                onStart(source);
                break;
            case ON_RESUME:
                onResume(source);
                break;
            case ON_PAUSE:
                onPause(source);
                break;
            case ON_STOP:
                onStop(source);
                break;
            case ON_DESTROY:
                onDestroy(source);
                break;
        }
    }

    public void onCreate(@NonNull LifecycleOwner owner) {
        initUI(owner);
    }

    public void onStart(@NonNull LifecycleOwner owner) {

    }

    public void onResume(@NonNull LifecycleOwner owner) {
        if (isGoRequestPermission) {
            startCamera();
        }
    }

    public void onPause(@NonNull LifecycleOwner owner) {
//        isShoudCreateOpen = false;
//        stopCamera();
    }

    public void onStop(@NonNull LifecycleOwner owner) {

    }

    public void onDestroy(@NonNull LifecycleOwner owner) {
        //这个地方可能有延迟
        releaseCamera();
    }

    /**
     * 初始化CameraScan
     */
    public void initCameraScan(@NonNull CameraScan<T> cameraScan) {
        if (initCameraScanCall != null) {
            initCameraScanCall.call(cameraScan);
        }
        if (cameraScan.getAnalyzer() == null) {
            cameraScan.setAnalyzer(createAnalyzer());
        }
        cameraScan.setOnCameraOpenCallback(cameraScan1 -> {
            onCameraOpen(cameraScan1);
        }).setOnScanResultCallback(this);

    }

    /**
     * 相机打开回调，每次销毁后重新启动都会回调
     */
    protected void onCameraOpen(@NonNull CameraScan<T> cameraScan) {
        if (mOnCameraOpenCallback != null) {
            mOnCameraOpenCallback.onCameraOpen(cameraScan);
        }
    }

    /**
     * 点击手电筒
     */
    protected boolean onClickFlashlight() {
        return toggleTorchState();
    }

    /**
     * 切换闪光灯状态（开启/关闭）
     */
    protected boolean toggleTorchState() {
        if (getCameraScan() != null) {
            boolean isTorch = getCameraScan().isTorchEnabled();
            getCameraScan().enableTorch(!isTorch);
            return !isTorch;
        }
        return false;
    }

    /**
     * 启动相机预览
     */
    public void startCamera() {

        if (mCameraScan != null) {
            if (CameraXPermissionUtils.checkPermission(getContext(), Manifest.permission.CAMERA)) {
                isGoRequestPermission = false;
                mCameraScan.startCamera();
            } else {
                isGoRequestPermission = true;
                CameraXPermissionUtils.requestPermission(getContext(), Manifest.permission.CAMERA, CAMERA_PERMISSION_REQUEST_CODE);
            }
        }
    }

    /**
     * 停止相机预览
     */
    public void stopCamera() {
        if (mCameraScan != null) {
            mCameraScan.stopCamera();
        }
    }

    /**
     * 释放相机
     */
    private void releaseCamera() {
        if (mCameraScan != null) {
            mCameraScan.release();
        }
    }

    /**
     * 布局ID；通过覆写此方法可以自定义布局
     *
     * @return 布局ID
     */
    public int getLayoutId() {
        return NO_ID;
    }

    /**
     * 预览视图{@link #previewView}的ID
     *
     * @return 预览视图ID
     */
    public abstract int getPreviewViewId();


    /**
     * 获取{@link CameraScan}
     *
     * @return {@link #mCameraScan}
     */
    public CameraScan<T> getCameraScan() {
        return mCameraScan;
    }


    /**
     * 创建{@link CameraScan}
     *
     * @param previewView {@link  PreviewView}
     * @return {@link CameraScan}
     */
    @NonNull
    public CameraScan<T> createCameraScan(LifecycleOwner owner, PreviewView previewView) {
        return new BaseCameraScan<>(getContext(), owner, previewView);
    }

    /**
     * 创建分析器
     *
     * @return {@link Analyzer}
     */
    @Nullable
    public Analyzer<T> createAnalyzer() {
        return null;
    }

    @Override
    public void onScanResultCallback(@NonNull AnalyzeResult<T> result) {
        if (scanResultCallback != null) {
            //这里如果开启多个，让用户选择，如果未开启，就取第一个
            scanResultCallback.call(result);
        }
    }

    public void setAnalyzeImage(boolean analyze) {
        getCameraScan().setAnalyzeImage(analyze);
    }

    public void setOnCameraOpenCallback(ScanCallback.OnCameraOpenCallback mOnCameraOpenCallback) {
        this.mOnCameraOpenCallback = mOnCameraOpenCallback;
    }
}